﻿using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.ComponentModel;

namespace Vit.Common.Extensions
{
    public static class ObjectExtension
    {
        public static object GetPropertyValue(this object obj, string propertyName)
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName, false);
            if (descriptor == null)
                descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName.ToPascalCase(), false);
            if (descriptor == null)
                descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName, true);
            if (descriptor == null)
                return null;
            return descriptor.GetValue(obj);
        }

        public static void SetPropertyValue(this object obj, string propertyName, object value)
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName, false);
            if (descriptor == null)
                descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName.ToPascalCase(), false);
            if (descriptor == null)
                descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName, true);
            if (descriptor == null)
                return;

            object val = null;
            if (value != null && !string.IsNullOrEmpty(value.ToString()))
            {
                if (descriptor.PropertyType.Equals(typeof(Guid)))
                {
                    val = new Guid(value.ToString());
                }
                else if (descriptor.PropertyType.Equals(typeof(System.Xml.Linq.XElement)))
                {
                    val = System.Xml.Linq.XElement.Parse(value.ToString());
                }
                else
                {
                    if (descriptor.PropertyType.Equals(typeof(bool)) || descriptor.PropertyType.Equals(typeof(Nullable<bool>)))
                    {
                        string strVal = value.ToString().ToLower();
                        if (strVal == "on" || strVal == "1" || strVal == "t" || strVal == "y" || strVal == "yes" || strVal == "true")
                            val = true;
                        else
                            val = false;
                    }
                    else
                    {
                        TypeConverter convert = TypeDescriptor.GetConverter(descriptor.PropertyType);
                        if (convert.CanConvertFrom(typeof(string)))
                            val = convert.ConvertFrom(value.ToString());
                    }
                }
            }
            descriptor.SetValue(obj, val);
        }

        public static void SetPropertyValues(this object obj, NameValueCollection values)
        {
            foreach(string key in values.Keys)
            {
                SetPropertyValue(obj, key, values[key]);
            }
        }

        public static void SetPropertyValues(this object obj, Dictionary<string, object> values)
        {
            foreach (KeyValuePair<string, object> kvp in values)
            {
                SetPropertyValue(obj, kvp.Key, kvp.Value);
            }
        }

        public static T Copy<T>(this T obj, NameValueCollection values) where T : class, new()
        {
            SetPropertyValues(obj, values);
            return obj;
        }

        public static T Copy<T>(this T obj, Dictionary<string, object> values) where T : class, new()
        {
            SetPropertyValues(obj, values);
            return obj;
        }

        public static T Copy<T>(this T obj) where T : class, new()
        {
            T o = new T();
            PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(obj);
            foreach (PropertyDescriptor descriptor in descriptors)
            {
                descriptor.SetValue(o, descriptor.GetValue(obj));
            }
            return o;
        }

        public static T ConvertTo<T>(this object obj, T defaultValue)
        {
            if (obj == null || obj == DBNull.Value)
                return defaultValue;

            if (typeof(T).IsEnum)
            {
                int val = -1;
                if (int.TryParse(obj.ToString(), out val))
                {
                    return (T)((object)val);
                }
                else
                {
                    return (T)Enum.Parse(typeof(T), obj.ToString());
                }
            }
            else if (typeof(Guid).IsAssignableFrom(typeof(T)))
            {
                return (T)(new Guid(obj.ToString()) as object);
            }
            else if(typeof(bool).IsAssignableFrom(typeof(T)))
            {
                switch (obj.ToString().ToLower())
                {
                    case "on":
                    case "yes":
                    case "1":
                    case "true":
                        return (T)(true as object);
                    default:
                        return (T)(false as object);
                }
            }
            else
            {
                try
                {
                    return (T)obj;
                }
                catch
                {
                    try
                    {
                        return (T)Convert.ChangeType(obj, typeof(T));
                    }
                    catch { }
                }
            }
            return defaultValue;
        }

        //public static object ConvertTo(this object obj, Type toType, object defaultValue)
        //{
        //    if (obj == null || obj == DBNull.Value)
        //        return defaultValue;
        //    try
        //    {
        //        return Convert.ChangeType(obj, toType);
        //    }
        //    catch { }
        //    return defaultValue;
        //}

        public static T OrEquals<T>(this T obj, T obj2)
        {
            if (typeof(T).Equals(typeof(String)))
            {
                return OrEquals<T>(obj, obj2, (o) => o != null && !string.IsNullOrEmpty(o.ToString()));
            }
            return OrEquals<T>(obj, obj2, (o) => o != null);
        }

        public static T OrEquals<T>(this T obj, T obj2, Func<T, bool> predicate)
        {
            if (predicate(obj))
                return obj;
            else
                return obj2;
        }

        public static object OrEquals(this object obj, object obj2)
        {
            return OrEquals(obj, obj2, (o) => o != null);
        }

        public static object OrEquals(this object obj, object obj2, Func<object, bool> predicate)
        {
            if (predicate(obj))
                return obj;
            else
                return obj2;
        }
    }
}
